package org.apache.lucene.ocean;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.index.IndexReader;
import org.apache.lucene.index.IndexWriter;
import org.apache.lucene.index.KeepOnlyLastCommitDeletionPolicy;
import org.apache.lucene.index.OceanSegmentReader;
import org.apache.lucene.index.SerialMergeScheduler;
import org.apache.lucene.ocean.WriteableMemoryIndex.MemoryIndexSnapshot;
import org.apache.lucene.store.Directory;
import org.apache.lucene.store.RAMDirectory;

/**
 * 
 *
 */
public class RamIndex extends DirectoryIndex {
	private RAMDirectory ramDirectory;
	private Long maxSnapshotId;
	private Long maxDocumentId;
  
	public RamIndex(IndexID id, List<? extends IndexSnapshot> indexSnapshots, TransactionSystem system) throws Exception {
		super(id, system);
		ramDirectory = new RAMDirectory();
		IndexReader[] indexReaders = getIndexReaders(indexSnapshots);
		RAMDirectory ramDirectory = new RAMDirectory();
		IndexWriter indexWriter = new IndexWriter(ramDirectory, false, system.getDefaultAnalyzer(), true);
		indexWriter.setMaxBufferedDocs(Integer.MAX_VALUE);
		indexWriter.setRAMBufferSizeMB(0.0f);
		indexWriter.setMergeScheduler(new SerialMergeScheduler());
		indexWriter.setUseCompoundFile(false);
		indexWriter.addIndexes(indexReaders);
		indexWriter.close();
		maxSnapshotId = getMaxSnapshotId(indexSnapshots);
		maxDocumentId = getMaxDocumentId(indexSnapshots);
		addMergedFromIndexIds(indexSnapshots);
	}
	
	// TODO: add timestamp so ramindex can be removed from indices
	public RamIndex(IndexID id, Long snapshotId, List<Deletes> deletesList, RAMDirectory ramDirectory, TransactionSystem system) throws Exception {
		super(id, system);
		this.ramDirectory = ramDirectory;
		initialIndexReader = IndexReader.open(ramDirectory, indexDeletionPolicy);
		if (deletesList != null) {
			for (Deletes deletes : deletesList) {
				applyDeletes(true, deletes, null, initialIndexReader);
			}
			initialIndexReader.flush();
		}
		createNewSnapshot(snapshotId, true, initialIndexReader);
	}
  
	// converts memoryIndexSnapshot into ramindexsnapshot
	public RamIndex(IndexID id, MemoryIndexSnapshot memoryIndexSnapshot) throws Exception, IOException {
		super(id, memoryIndexSnapshot.getIndex().getSystem());
		this.maxSnapshotId = memoryIndexSnapshot.getMaxSnapshotId();
		this.maxDocumentId = memoryIndexSnapshot.getMaxDocumentId();
		ramDirectory = new RAMDirectory();
		Analyzer defaultAnalyzer = memoryIndexSnapshot.getIndex().getSystem().getDefaultAnalyzer();
		IndexWriter indexWriter = new IndexWriter(ramDirectory, false, defaultAnalyzer, true, new KeepOnlyLastCommitDeletionPolicy());
		indexWriter.setMaxBufferedDocs(Integer.MAX_VALUE);
		indexWriter.setRAMBufferSizeMB(IndexWriter.DISABLE_AUTO_FLUSH);
		indexWriter.setMergeScheduler(new SerialMergeScheduler());
    indexWriter.setUseCompoundFile(false);
		indexWriter.addIndexes(new IndexReader[] {memoryIndexSnapshot.getIndexReader()});
		indexWriter.close();
		initialIndexReader = IndexReader.open(ramDirectory, indexDeletionPolicy);
		addMergedFromIndexId(memoryIndexSnapshot);
		List<IndexSnapshot> indexSnapshots = new ArrayList<IndexSnapshot>(1);
		indexSnapshots.add(memoryIndexSnapshot);
		createNewSnapshot(memoryIndexSnapshot.getSnapshotId(), true, initialIndexReader);
	}
  
	public RamIndexSnapshot commitIndex(Transaction transaction) throws IndexException, InterruptedException, IOException {
		try {
			transaction.ready(this);
			if (transaction.go()) {
				Long snapshotId = transaction.getId();
				RamIndexSnapshot indexSnapshot = (RamIndexSnapshot)createNewSnapshot(snapshotId, true, initialIndexReader);
				return indexSnapshot;
			} else {
				// if commit fails this snapshot and ramindex won't make it
				return null;
			}
		} catch (Throwable throwable) {
			LOG.error("", throwable);
			transaction.failed(this, throwable);
			return null;
		}
	}
	
	protected RamIndexSnapshot doCreateNewSnapshot(Long snapshotId, IndexReader newIndexReader) throws IOException {
		RamIndexSnapshot ramIndexSnapshot = new RamIndexSnapshot(snapshotId, (OceanSegmentReader)newIndexReader);
		registerSnapshot(ramIndexSnapshot);
		return ramIndexSnapshot;
	}

	public class RamIndexSnapshot extends DirectoryIndexSnapshot {
		public RamIndexSnapshot(Long snapshotId, OceanSegmentReader indexReader) throws IOException {
			super(snapshotId, indexReader);
		}
		
		public String toString() {
		  return "RamIndexSnapshot index: "+RamIndex.this.getId()+" snapshotid: "+snapshotId+" maxDoc: "+indexReader.maxDoc();
		}
	}

	public Directory getDirectory() {
		return ramDirectory;
	}
}
